package simonxianyu.boot.mybatis.interceptor;

import simonxianyu.boot.mybatis.dialect.Dialect;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationStatementInterceptor extends AbstractPaginationInterceptor implements Interceptor {

    private final static Logger log = LoggerFactory
            .getLogger(PaginationStatementInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        ParameterHandler parameterHandler = statementHandler.getParameterHandler();
        Object parameterObject = parameterHandler.getParameterObject();

        Pageable pagination;
        if (parameterObject instanceof Pageable) {
            pagination = (Pageable) parameterObject;
        } else {
            pagination = makePageable(parameterObject);
        }

        if (pagination != null) {
            MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, defaultObjectFactory,
                defaultObjectWrapperFactory, defaultReflectorFactory);
            Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
            Dialect.Type databaseType = null;

            try {
                databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
            } catch (Exception e) {
                throw new Exception("Generate SQL: Obtain DatabaseType Failed!");
            }

            Dialect dialect = Dialect.parse(databaseType);
            if (null == dialect) {
                throw new Exception("no proper dialect");
            }

            String originalSql = (String) metaStatementHandler.getValue("boundSql.sql");

            metaStatementHandler.setValue("boundSql.sql", dialect.getLimitString(originalSql, pagination.getPageNumber() * pagination.getPageSize(), pagination.getPageSize()));

            if (log.isDebugEnabled()) {
                BoundSql boundSql = statementHandler.getBoundSql();
                log.debug("Generate SQL : " + boundSql.getSql());
            }

            return invocation.proceed();
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
